package helper

import (
	"archive/zip"
	"bytes"
	"cgs-server/pkg/file"
	"fmt"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

// Zip reads all the files in the directory and creates a new compressed file.
func Zip(dir, path string) error {
	// check dir
	stat, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return fmt.Errorf("%v is not exist", dir)
	}
	if !stat.IsDir() {
		return fmt.Errorf("%v is not a dir", dir)
	}

	// check path
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	// create zip file
	w := zip.NewWriter(file)
	defer w.Close()

	// func to create zip file
	walkFunc := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if path == dir { // The first path of filepath.Walk is always the root.
			return nil
		}
		targetPath := strings.TrimLeft(path, dir)
		if info.IsDir() { // dir
			if _, err := w.Create(targetPath + "/"); err != nil {
				return err
			}
		} else { // file
			bytes, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			target, err := w.Create(targetPath)
			if err != nil {
				return err
			}
			if _, err := target.Write(bytes); err != nil {
				return err
			}
		}

		return nil
	}

	// walk all the directories and files in the dir
	if err := filepath.Walk(dir, walkFunc); err != nil {
		return err
	}

	return nil
}

// UnZip decompresses all files to a directory.
func UnZip(filename, dir string) error {
	r, err := zip.OpenReader(filename)
	if err != nil {
		return err
	}
	defer r.Close()

	var name string
	for _, f := range r.File {
		// 乱码问题
		if f.Flags == 0 {
			// 如果标志位(f.Flags)为0，则默认时本地编码，默认为 gbk
			i := bytes.NewReader([]byte(f.Name))
			decoder := transform.NewReader(i, simplifiedchinese.GB18030.NewDecoder())
			content, _ := ioutil.ReadAll(decoder)
			name = string(content)
		} else {
			// 如果标志位是 1 << 11 也就是2048，则是utf-8编码
			name = f.Name
		}
		//name := f.Name
		info := f.FileInfo()
		targetPath := filepath.Join(dir, name)
		if info.IsDir() { // dir
			if _, err := os.Stat(targetPath); os.IsNotExist(err) {
				// IMPORTANT: info.Mode() return wrong mode when f is a dir.
				// So, DO NOT use info.Mode() to os.MkdirAll.
				if err := os.MkdirAll(targetPath, 0755); err != nil {
					return err
				}
			}
		} else { // file
			err := writeZipFile(f, targetPath, info.Mode())
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func writeZipFile(f *zip.File, targetPath string, mode fs.FileMode) error {
	reader, err := f.Open()
	if err != nil {
		return err
	}
	defer reader.Close()
	// 创建对应的文件夹
	err = file.IsNotExistMkDir(filepath.Dir(targetPath))
	if err != nil {
		return err
	}
	writer, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, mode)
	if err != nil {
		return err
	}
	defer writer.Close()
	if _, err := io.Copy(writer, reader); err != nil {
		return err
	}
	return nil
}

/*func ReadZipFile(data []byte) ([]*model.ZipFileData, error) {
	reader := bytes.NewReader(data)
	zipData := []*model.ZipFileData{}
	r,_ := zip.NewReader(reader, int64(len(data)))
	for _, f := range r.File {
		name := f.Name
		info := f.FileInfo()
		if info.IsDir() { // dir

		} else { // file
			read, err := f.Open()
			if err != nil {
				return nil, err
			}
			defer read.Close()
			m, err := ioutil.ReadAll(read)

			zipData = append(zipData, &model.ZipFileData{Name: name, Body: m})
		}
	}
	return zipData,nil
}

func ZipFile(dataArr []*model.ZipFileData) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	w := zip.NewWriter(buf)
	defer w.Close()
	for _, file := range dataArr {
		f, err := w.Create(file.Name)
		if err != nil {
			return nil, err
		}
		_, err = f.Write(file.Body)
		if err != nil {
			return nil, err
		}
	}
	return buf, nil
}

func ZipFileToBuffer(dir string) (*bytes.Buffer, error) {
	// check dir
	stat, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return nil, fmt.Errorf("%v is not exist", dir)
	}
	if !stat.IsDir() {
		return nil, fmt.Errorf("%v is not a dir", dir)
	}

	dir = strings.ReplaceAll(dir, "/", string(filepath.Separator))

	var fileData []*model.ZipFileData
	// func to create zip file
	walkFunc := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if path == dir || strings.HasSuffix(path, ".zip") { // The first path of filepath.Walk is always the root.
			return nil
		}
		trimPath := strings.TrimPrefix(path, dir)
		if info.IsDir() { // dir

		} else { // file
			byteData, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			fileData = append(fileData, &model.ZipFileData{
				Name: trimPath,
				Body:  byteData,
			})
		}
		return nil
	}

	// walk all the directories and files in the dir
	if err := filepath.Walk(dir, walkFunc); err != nil {
		return nil, err
	}

	return ZipFile(fileData)
}*/
